package YPTServerRegister

import (
	pb "git.oschina.net/YPTArch/ypt_service_discovery/etcd"
	"context"
	"fmt"
	"github.com/robfig/cron"
	"io"
	"encoding/json"
)

type RegWatchCli struct {
	HeartBeatTick string
	r *Register
	sKey string

}
type regWatchData struct{
	Event string
	Code string
	Host string
	Port int
}

type DiscoveryWatchCli struct{
	d *DiscoveryClient
}
var watchClient pb.WatchClient   //一个服务可能会用到其他多个微服务，这里做一个复用

var codesMap map[string] []regWatchData // 本地保存一份所有code的信息,与服务器上面的同步,注册watch中使用


func init(){
	codesMap = make(map[string] []regWatchData)
}
func NewRegWatchCli(r *Register) (*RegWatchCli){
	if watchClient == nil {
		watchClient = pb.NewWatchClient(r.con)
	}
	reg := &RegWatchCli{
		HeartBeatTick : "15s",
		r : r,
		sKey : r.RegisterKey + "/code/" + r.Code,
	}
	wd := regWatchData{
		Event : "new",
		Code : r.Code,
		Host : r.rInfo.Host,
		Port : r.rInfo.Port,
	}

	reg.addLeaseKey(wd)
	reg.heartBeat()
	return reg
}

func (w *RegWatchCli) addLeaseKey(wd regWatchData) ( error){
	leaseClient := NewLease(w.r.con)
	id, err := leaseClient.GrantLease()
	if err != nil{
		return err
	}
	req ,err := json.Marshal(wd)
	if err != nil{
		return err
	}
	_, err = w.r.kvClient.Put(context.Background(),&pb.PutRequest{
		Key : []byte(w.sKey),
		Value : req,
		Lease : id,
	})
	return nil
}

func (w *RegWatchCli) heartBeat(){
	c := cron.New()
	spec := "@every " + w.HeartBeatTick
	wd := regWatchData{
		Event : "update",
		Code : w.r.Code,
		Host : w.r.rInfo.Host,
		Port : w.r.rInfo.Port,
	}
	c.AddFunc(spec, func(){
		w.addLeaseKey(wd)
	})
	c.Start()
}

func (w *RegWatchCli) processDelete(key string,wd regWatchData){
	res, err := w.r.kvClient.Range(context.Background(),&pb.RangeRequest{//获取服务信息，不是code信息
		Key : []byte(key),
	})
	if err != nil || res.Kvs == nil{
		return
	}
	var serverData EtcdSavedData//远程的数据
	if err = json.Unmarshal(res.Kvs[0].Value,&serverData);err != nil{
		return
	}
	codesList := make([]string,0)

	//服务正常停止
	// watch code 信息在serverData中不存在，则表示正常停止
	exists := false
	for _,v := range serverData.ContainerCode{
		if v == wd.Code{
			exists = true
		}else{
			codesList = append(codesList,v)
		}

	}

	codesMapList := make([]regWatchData,0)
	for _,v := range codesMap[key]{
		if v.Code != wd.Code{
			codesMapList = append(codesMapList,regWatchData{
				Event : v.Event,
				Code : v.Code,
				Host : v.Host,
				Port : v.Port,
			})
			//codesList = append(codesList, v.Code)
		}
	}
	codesMap[key] = codesMapList //更新本地保存的code信息

	if !exists{
		return
	}

	//服务异常停止

	canDeleteHost := true //是否删除host
	for _,v := range codesMap[key]{
		if v.Host == wd.Host && v.Port == wd.Port{ //删除掉code信息后还存在其他code拥有相同的host，则不删除
			canDeleteHost = false
			break
		}
	}
	addrs := make([]Address,0)
	if canDeleteHost{
		for _,v := range serverData.Addr{
			if v.Host == wd.Host && v.Port == wd.Port{
				continue
			}
			addrs = append(addrs,v)
		}
		//etcdResMap[key].Addr = addrs
		serverData.Addr = addrs
	}
	serverData.ContainerCode = codesList

	jsonD,err := json.Marshal(serverData)
	if err != nil{
		return
	}
	w.r.kvClient.Put(context.Background(),&pb.PutRequest{
		Key : []byte(key),
		Value : jsonD,
	})
}

func (w *RegWatchCli) processNew(key string, wd regWatchData){
	codesMap[w.r.RegisterKey] = append(codesMap[w.r.RegisterKey],wd)
}


func NewDiscWatchClient(d *DiscoveryClient) (*DiscoveryWatchCli,error){
	if watchClient == nil {
		watchClient = pb.NewWatchClient(d.con)
	}
	return &DiscoveryWatchCli{
		d : d,
	},nil
}

func (dc *DiscoveryWatchCli) processDelete(key string,wd regWatchData){
	//最后一个服务死掉
	if len(etcdResMap[key].ContainerCode) == 1 && wd.Code == etcdResMap[key].ContainerCode[0]{
		dc.d.kvClient.DeleteRange(context.Background(),&pb.DeleteRangeRequest{
			Key : []byte(key),
		})
		etcdResMap[key].Addr = []Address{}
		etcdResMap[key].ContainerCode = []string{}
		return
	}

	codesList := make([]string,0)
	//获取现有的code
	for _,v := range etcdResMap[key].ContainerCode{
		if v != wd.Code{
			codesList = append(codesList,v)
		}
	}

	etcdResMap[key].ContainerCode = codesList
	codesMapList := make([]regWatchData,0)
	for _,v := range codesMap[key]{
		if v.Code != wd.Code{
			codesMapList = append(codesMapList,regWatchData{
				Event : v.Event,
				Code : v.Code,
				Host : v.Host,
				Port : v.Port,
			})
		}
	}
	codesMap[key] = codesMapList
	canDeleteHost := true //是否删除host
	for _,v := range codesMap[key]{
		if v.Host == wd.Host && v.Port == wd.Port{ //删除掉code信息后还存在其他code拥有相同的host，则不删除
			canDeleteHost = false
			break
		}
	}
	addrs := make([]Address,0)
	if canDeleteHost{
		for _,v := range etcdResMap[key].Addr{
			if v.Host == wd.Host && v.Port == wd.Port{ //去除已经删除的host
				continue
			}
			addrs = append(addrs,v)
		}
		etcdResMap[key].Addr = addrs
	}

}
func (dc *DiscoveryWatchCli) processNew(key string,wd regWatchData){
	canInsert := true
	for _,v := range etcdResMap[key].Addr{
		if v.Host == wd.Host && v.Port == wd.Port{
			canInsert = false
			break
		}
	}
	if canInsert{
		etcdResMap[key].Addr = append(etcdResMap[key].Addr,Address{
			Host : wd.Host,
			Port : wd.Port,
		})
	}
	etcdResMap[key].ContainerCode = append(etcdResMap[key].ContainerCode,wd.Code)
	codesMap[key] = append(codesMap[key],wd)
}

//获取code信息，与事件
func checkHeartBeatData(key string,res *pb.WatchResponse) (string,regWatchData,error){
	var event string
	var wd regWatchData
	if res.Events != nil {
		if res.Events[0].Type.String() == "DELETE"{
			event = "delete"
			keyName := string(res.Events[0].Kv.Key)
			code := keyName[len(keyName) - 32:]
			for _, v := range codesMap[key]{
				if v.Code == code{
					wd = v
				}
			}
		}else{
			if err := json.Unmarshal(res.Events[0].Kv.Value,&wd);err != nil {
				return "",regWatchData{},err
			}
			event = wd.Event
		}



	}else{
		return "",regWatchData{}, nil
	}
	return event,wd, nil
}

func watch(key string, kvClient pb.KVClient, funcDel func (string, regWatchData), funcNew func (string, regWatchData)) error{
	var startKey = key + "/code/"
	var rangeKey = key + "/code/" + get32Z()
	codesData,err := kvClient.Range(context.Background(),&pb.RangeRequest{
		Key : []byte(startKey),
		RangeEnd : []byte(rangeKey),
	})

	if err != nil {
		return err
	}
	if codesData.Kvs != nil {
		temp := make([]regWatchData,0)
		for _,v := range codesData.Kvs{
			var t regWatchData
			if err := json.Unmarshal(v.Value,&t);err != nil{
				return err
			}
			temp = append(temp,t)
		}
		codesMap[key] = temp
	}
	watch , err := watchClient.Watch(context.Background())
	if err != nil {
		return err
	}
	waitc := make(chan struct{})
	go func() {
		for{
			in, err := watch.Recv()
			if err == io.EOF{
				fmt.Println("err eof")
				close(waitc)
				return
			}
			if err != nil {
				fmt.Println("err err",err)
				return
			}
			event ,wd  ,err := checkHeartBeatData(key,in)
			if err != nil{
				return
			}
			switch event {
				case "delete" : {
					funcDel(key, wd)
					//processDelete(key,wd,dc.d.kvClient)
				}
				case "new" : {
					funcNew(key, wd)
					//dc.processNew(key,wd)
				}
				case "update" : {
					//ignore
				}
			}

		}
	}()
	req := pb.WatchRequest{
		RequestUnion : &pb.WatchRequest_CreateRequest{
			CreateRequest : &pb.WatchCreateRequest{
				Key : []byte(startKey),
				RangeEnd : []byte(rangeKey),
			},
		},
	}
	err = watch.Send(&req)
	watch.CloseSend()
	<-waitc
	return nil
}
